#include "mod-ether.hpp"

const static bool DEBUG = true;

Record * EtherDecoder::decode (Packet & pkt, Record & prev)
{
  EthHdr * hdr = (EthHdr*) pkt.getCur ();
  Node * src, * dst;
  
  if (! this->rec.lock ())
    return NULL;

  if (NULL == hdr || ! pkt.moveCur (sizeof (EthHdr)))
    return NULL;

  this->rec.setData (DEC_ETHER, hdr, sizeof (EthHdr));
  src = this->node->fetch (&hdr->etherSrcHost, ETHER_ADDR_LEN);
  dst = this->node->fetch (&hdr->etherDstHost, ETHER_ADDR_LEN);

  switch (htons (hdr->etherType))
    {
    case ETHERTYPE_ARP:  this->rec.setNextDecoder (DEC_NULL); break;
    case ETHERTYPE_VLAN: this->rec.setNextDecoder (DEC_NULL); break;
    case ETHERTYPE_IP:   this->rec.setNextDecoder (DEC_IPV4); break;
    case ETHERTYPE_IPV6: this->rec.setNextDecoder (DEC_IPV6); break;
    default:
      /* ignore */
      break;
    }

  return &(this->rec);
}


NDecFilter * EtherDecoder::makeFilter (string &name)
{
  EtherFilter * f = NULL;

  if (name == "ether.src_addr")
    f = new EtherFilter (EtherFilter::FT_SRC_ADDR);
  else if (name == "ether.dst_addr")
    f = new EtherFilter (EtherFilter::FT_DST_ADDR);

  return f;
}



bool EtherFilter::setup (string &arg)
{
  assert (arg != "");

  try 
    {
      const char * p = arg.c_str ();
      const char * e = arg.c_str () + arg.length ();
      int i = 0;
      while (p + 1 < e && i < 6)
	{
	  char buf[] = "xx", * err;
	  buf[0] = *p;
	  buf[1] = *(p + 1);
	  int n = strtoul (buf, &err, 16);
	  if (err != &buf[2])
	    throw string ("Invalid ethernet address foramt '" + arg + "'");

	  this->macAddr [i] = n;
	  i += 1;
	  p += 2;
	}      
    }
  catch (string e)
    {
      this->setErrMsg ("[EtherFilter] " + e);
      return false;
    }

  this->any = false;
  return true;
}

void EtherFilter::check (NDecResult &res)
{
  Record * rec = res.getRecord (DEC_ETHER);
  EthHdr * hdr;
  void * tgt = NULL;

  if (NULL == rec|| 
      NULL == (hdr = static_cast<EthHdr*> (rec->getPtr ())))
    return ;

  switch (this->type)
    {
    case FT_SRC_ADDR: tgt = hdr->etherSrcHost; break;
    case FT_DST_ADDR: tgt = hdr->etherDstHost; break;
    }

  assert (tgt);

  if (this->any || memcmp (tgt, this->macAddr, ADDR_LEN))
    this->pushResult (tgt, ADDR_LEN);
}
